﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Rhino.Mocks;

namespace ExtendedMvc.Tests {
	public static class MvcMockHelpers {
		public static IViewEngine FakeViewEngine(string viewName, string viewContent) {
			var mocks = new MockRepository();
			var engine = mocks.FakeViewEngine(viewName, viewContent);
			mocks.ReplayAll();
			return engine;
		}

		public static ControllerContext FakeControllerContext() {
			var mocks = new MockRepository();
			var context = mocks.FakeControllerContext();
			mocks.ReplayAll();
			return context;
		}

		public static HttpContextBase FakeHttpContext() {
			var mocks = new MockRepository();
			var context = mocks.FakeHttpContext();
			mocks.ReplayAll();
			return context;
		}

		public static HttpContextBase FakeHttpContext(string requestUrl) {
			var mocks = new MockRepository();
			var context = mocks.FakeHttpContext();
			var uri = new Uri(requestUrl);
			SetupResult.For(context.Request.Url).Return(uri);
			SetupResult.For(context.Request.Path).Return(uri.LocalPath);
			mocks.ReplayAll();
			return context;
		}

		private static HttpContextBase FakeHttpContext(this MockRepository mocks) {
			return mocks.FakeHttpContext(mocks.FakeHttpRequest(), mocks.FakeHttpResponse(), mocks.FakeHttpSessionState(), mocks.FakeHttpServerUtility(), mocks.FakeIPrincipal(), mocks.FakeItems());
		}

		private static IViewEngine FakeViewEngine(this MockRepository mocks, string viewName, string viewContent) {
			var engine = mocks.DynamicMock<IViewEngine>();
			var view = mocks.DynamicMock<IView>();
			Action<ViewContext, TextWriter> action = (v, w) => w.Write(viewContent);
			engine.Expect(x => x.FindPartialView(Arg<ControllerContext>.Is.Anything, Arg<string>.Is.Equal(viewName), Arg<bool>.Is.Anything)).Return(new ViewEngineResult(view, engine)).Repeat.Any();
			engine.Expect(x => x.FindView(Arg<ControllerContext>.Is.Anything, Arg<string>.Is.Equal(viewName), Arg<string>.Is.Anything, Arg<bool>.Is.Anything)).Return(new ViewEngineResult(view, engine)).Repeat.Any();
			view.Expect(x => x.Render(null, null)).IgnoreArguments().Do(action).Repeat.Any();
			return engine;
		}

		private static ControllerContext FakeControllerContext(this MockRepository mocks) {
			var context = mocks.DynamicMock<ControllerContext>();
			mocks.Replay(context);
			return context;
		}

		private static HttpContextBase FakeHttpContext(this MockRepository mocks, HttpRequestBase request, HttpResponseBase response, HttpSessionStateBase session, HttpServerUtilityBase server, IPrincipal user, IDictionary items) {
			var context = mocks.DynamicMock<HttpContextBase>();
			SetupResult.For(context.User).Return(user);
			SetupResult.For(context.Request).Return(request);
			SetupResult.For(context.Response).Return(response);
			SetupResult.For(context.Session).Return(session);
			SetupResult.For(context.Server).Return(server);
			SetupResult.For(context.Items).Return(items);
			mocks.Replay(context);
			return context;
		}

		private static HttpRequestBase FakeHttpRequest(this MockRepository mocks) {
			var request = mocks.DynamicMock<HttpRequestBase>();
			var browser = mocks.DynamicMock<HttpBrowserCapabilitiesBase>();
			var form = new NameValueCollection();
			var queryString = new NameValueCollection();
			var cookies = new HttpCookieCollection();
			var serverVariables = new NameValueCollection();
			var headers = new NameValueCollection();

			SetupResult.For(request.Url).Return(new Uri("http://localhost/AppName/Home/Index.aspx"));
			SetupResult.For(request.RawUrl).Return("/Home/Index.aspx");
			SetupResult.For(request.ApplicationPath).Return("/");
			SetupResult.For(request.Form).Return(form);
			SetupResult.For(request.QueryString).Return(queryString);
			SetupResult.For(request.Cookies).Return(cookies);
			SetupResult.For(request.ServerVariables).Return(serverVariables);
			SetupResult.For(request.Params).Do((Func<NameValueCollection>)(() => CreateParams(queryString, form, cookies, serverVariables)));
			SetupResult.For(request.Browser).Return(browser);
			SetupResult.For(request.Headers).Return(headers);

			return request;
		}

		private static NameValueCollection CreateParams(NameValueCollection queryString, NameValueCollection form, HttpCookieCollection cookies, NameValueCollection serverVariables) {
			NameValueCollection parms = new NameValueCollection(48);
			parms.Add(queryString);
			parms.Add(form);
			for (var i = 0; i < cookies.Count; i++) {
				var cookie = cookies.Get(i);
				parms.Add(cookie.Name, cookie.Value);
			}
			parms.Add(serverVariables);
			return parms;
		}

		private static HttpResponseBase FakeHttpResponse(this MockRepository mocks) {
			var response = mocks.DynamicMock<HttpResponseBase>();
			SetupResult.For(response.OutputStream).Return(new MemoryStream());
			SetupResult.For(response.Output).Return(new StringWriter());
			SetupResult.For(response.ContentType).PropertyBehavior();
			SetupResult.For(response.StatusCode).PropertyBehavior();
			SetupResult.For(response.RedirectLocation).PropertyBehavior();
			SetupResult.For<string>(response.ApplyAppPathModifier(Arg<string>.Is.Anything)).IgnoreArguments().Do((Func<string, string>)((a) => { return a; }));

			return response;
		}
		private static HttpSessionStateBase FakeHttpSessionState(this MockRepository mocks) {
			var session = mocks.DynamicMock<HttpSessionStateBase>();
			return session;
		}

		private static HttpServerUtilityBase FakeHttpServerUtility(this MockRepository mocks) {
			var server = mocks.DynamicMock<HttpServerUtilityBase>();
			return server;
		}

		private static IPrincipal FakeIPrincipal(this MockRepository mocks) {
			var principal = mocks.DynamicMock<IPrincipal>();
			return principal;
		}

		private static IDictionary FakeItems(this MockRepository mocks) {
			var items = new Dictionary<object, object>();
			return items;
		}

		public static ViewContext FakeViewContext() {
			var mocks = new MockRepository();
			var context = mocks.FakeViewContext();
			mocks.ReplayAll();
			return context;
		}

		private static ViewContext FakeViewContext(this MockRepository mocks) {
			var httpContext = FakeHttpContext(mocks);
			var controller = mocks.DynamicMock<ControllerBase>();
			var view = mocks.DynamicMock<IView>();
			if (!mocks.IsInReplayMode(httpContext)) mocks.Replay(httpContext);
			var controllerContext = new ControllerContext(httpContext, new RouteData(), controller);
			var viewContext = new ViewContext(controllerContext, view, new ViewDataDictionary(), new TempDataDictionary(), new StringWriter());
			viewContext.FormContext = new FormContext();
			return viewContext;
		}

		public static TController SetupControllerContext<TController>(this TController controller, string viewContent = null) where TController : Controller {
			var controllerContext = new ControllerContext(FakeHttpContext(), new RouteData(), controller);
			controller.ControllerContext = controllerContext;
			return controller;
		}

		public static ModelViewPage FakeViewPage(string viewName, string controllerName = null, string viewContent = null) {
			var view = new ModelViewPage();
			view.ViewContext = MvcMockHelpers.FakeViewContext();
			view.ViewContext.ViewData = new ViewDataDictionary();
			view.ViewData = view.ViewContext.ViewData;
			if (controllerName != null) view.ViewContext.RouteData.Values["controller"] = controllerName;
			return view;
		}

		public static ModelViewUserControl FakePartialView(string viewName, string controllerName = null, string viewContent = null) {
			var view = new ModelViewUserControl();
			view.ViewContext = MvcMockHelpers.FakeViewContext();
			view.ViewContext.ViewData = new ViewDataDictionary();
			view.ViewData = view.ViewContext.ViewData;
			if (controllerName != null) view.ViewContext.RouteData.Values["controller"] = controllerName;
			return view;
		}

		public static ModelViewPage<TModel> FakeViewPage<TModel>(string viewName, TModel model, string controllerName = null, string viewContent = null) where TModel : class {
			var mocks = new MockRepository();
			var view = new ModelViewPage<TModel>();
			view.ViewContext = mocks.FakeViewContext();
			view.ViewContext.ViewData = new ViewDataDictionary<TModel>(model);
			view.ViewData = (view.ViewContext.ViewData as ViewDataDictionary<TModel>);
			if (controllerName != null) view.ViewContext.RouteData.Values["controller"] = controllerName;
			mocks.ReplayAll();
			return view;
		}

		public static ModelViewPage<IEnumerable<TModel>> FakeViewPage<TModel>(string viewName, IEnumerable<TModel> list, string controllerName = null, string viewContent = null) where TModel : class {
			var view = new ModelViewPage<IEnumerable<TModel>>();
			view.ViewContext = MvcMockHelpers.FakeViewContext();
			view.ViewContext.ViewData = new ViewDataDictionary<IEnumerable<TModel>>(list);
			view.ViewData = (view.ViewContext.ViewData as ViewDataDictionary<IEnumerable<TModel>>);
			if (controllerName != null) view.ViewContext.RouteData.Values["controller"] = controllerName;
			return view;
		}

		public static ModelViewUserControl<TModel> FakePartialView<TModel>(string viewName, TModel model, string controllerName = null, string viewContent = null) where TModel : class {
			var mocks = new MockRepository();
			var view = new ModelViewUserControl<TModel>();
			view.ViewContext = mocks.FakeViewContext();
			view.ViewContext.ViewData = new ViewDataDictionary<TModel>(model);
			view.ViewData = (view.ViewContext.ViewData as ViewDataDictionary<TModel>);
			if (controllerName != null) view.ViewContext.RouteData.Values["controller"] = controllerName;
			mocks.ReplayAll();
			return view;
		}

		public static void SetViewContent(HttpServerUtilityBase server, string viewContent) {
			Action<IHttpHandler, TextWriter, bool> pageWriter = null;
			if (viewContent != null) pageWriter = (h, w, b) => { w.Write(viewContent); };
			if (pageWriter != null) server.Expect(x => x.Execute(Arg<IHttpHandler>.Is.Anything, Arg<TextWriter>.Is.Anything, Arg<bool>.Is.Anything)).Do(pageWriter);
			server.Replay();
		}
	}
}
